package com.duojuhe.coremodule.topo.service.impl;

import com.duojuhe.common.annotation.DataScopeFilter;
import com.duojuhe.common.annotation.KeyLock;
import com.duojuhe.common.bean.DataScopeFilterBean;
import com.duojuhe.common.bean.UserTokenInfoVo;
import com.duojuhe.common.constant.DataScopeConstants;
import com.duojuhe.common.enums.SystemEnum;
import com.duojuhe.common.exception.base.DuoJuHeException;
import com.duojuhe.common.result.ErrorCodes;
import com.duojuhe.common.result.PageResult;
import com.duojuhe.common.result.ServiceResult;
import com.duojuhe.common.utils.idgenerator.UUIDUtils;
import com.duojuhe.common.utils.page.PageHelperUtil;
import com.duojuhe.common.utils.printwriter.PrintWriterUtil;
import com.duojuhe.coremodule.BaseService;
import com.duojuhe.coremodule.topo.entity.TopoTopology;
import com.duojuhe.coremodule.topo.mapper.TopoElementClassMapper;
import com.duojuhe.coremodule.topo.mapper.TopoElementInfoMapper;
import com.duojuhe.coremodule.topo.mapper.TopoTopologyMapper;
import com.duojuhe.coremodule.topo.pojo.dto.elementclass.InitTopoElementClassRes;
import com.duojuhe.coremodule.topo.pojo.dto.elementclass.SelectTopoElementClassRes;
import com.duojuhe.coremodule.topo.pojo.dto.elementinfo.QueryTopoElementInfoRes;
import com.duojuhe.coremodule.topo.pojo.dto.topology.*;
import com.duojuhe.coremodule.topo.service.TopoTopologyService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TopoTopologyServiceImpl extends BaseService implements TopoTopologyService {
    @Resource
    private TopoElementClassMapper topoElementClassMapper;
    @Resource
    private TopoElementInfoMapper topoElementInfoMapper;
    @Resource
    private TopoTopologyMapper topoTopologyMapper;
    /**
     * 【分页查询】分页查询拓扑图list
     * @param req
     * @return
     */
    @DataScopeFilter(dataScope = DataScopeConstants.SELF_DATA)
    @Override
    public ServiceResult<PageResult<List<QueryTopoTopologyPageRes>>> queryTopoTopologyPageResList(QueryTopoTopologyPageReq req) {
        PageHelperUtil.orderByAndStartPage(req, "createTime desc, topoId desc");
        List<QueryTopoTopologyPageRes> list = topoTopologyMapper.queryTopoTopologyPageResList(req);
        return PageHelperUtil.returnServiceResult(req,list);
    }
    /**
     * 【保存】拓扑图信息
     * @param req
     * @return
     */
    @Override
    public ServiceResult saveTopoTopology(SaveTopoTopologyReq req) {
        //当前登录人
        UserTokenInfoVo userTokenInfoVo = getCurrentLoginUserInfoDTO();
        //topo图id
        String topoId = UUIDUtils.getUUID32();
        //当前时间
        Date nowDate = new Date();
        TopoTopology topology = new TopoTopology();
        topology.setTopoId(topoId);
        topology.setTopoName(req.getTopoName());
        topology.setCoverImg(req.getCoverImg());
        topology.setCreateTime(nowDate);
        topology.setUpdateTime(nowDate);
        topology.setRemark(req.getRemark());
        topology.setXmlData("");
        topology.setCreateTime(nowDate);
        topology.setCreateUserId(userTokenInfoVo.getUserId());
        topology.setUpdateTime(nowDate);
        topology.setUpdateUserId(userTokenInfoVo.getUserId());
        topology.setCreateDeptId(userTokenInfoVo.getCreateDeptId());
        topology.setTenantId(userTokenInfoVo.getTenantId());
        topoTopologyMapper.insertSelective(topology);
        return ServiceResult.ok(topoId);
    }
    /**
     * 【修改】拓扑图信息
     * @param req
     * @return
     */
    @Override
    public ServiceResult updateTopoTopology(UpdateTopoTopologyReq req) {
        TopoTopology topoTopology = topoTopologyMapper.selectByPrimaryKey(req.getTopoId());
        if (topoTopology==null){
            return ServiceResult.fail(ErrorCodes.TOPO_LOGY_NOT_EXIST);
        }
        //当前时间
        Date nowDate = new Date();
        TopoTopology topology = new TopoTopology();
        topology.setTopoId(req.getTopoId());
        topology.setTopoName(req.getTopoName());
        topology.setCoverImg(req.getCoverImg());
        topology.setUpdateTime(nowDate);
        topology.setUpdateUserId(getCurrentLoginUserId());
        topology.setRemark(req.getRemark());
        topoTopologyMapper.updateByPrimaryKeySelective(topology);
        return ServiceResult.ok(req.getTopoId());
    }


    /**
     * 【删除】根据拓扑图ID删除拓扑图，注意一次仅能删除一个
     * @param req
     * @return
     */
    @KeyLock(lockKeyParts = "topoId")
    @Override
    public ServiceResult deleteTopoTopologyByTopoId(TopoTopologyIdReq req) {
        TopoTopology topoTopology = topoTopologyMapper.selectByPrimaryKey(req.getTopoId());
        if (topoTopology==null){
            return ServiceResult.fail(ErrorCodes.TOPO_LOGY_NOT_EXIST);
        }
        topoTopologyMapper.deleteByPrimaryKey(req.getTopoId());
        return ServiceResult.ok(req.getTopoId());
    }

    /**
     * 【设计】设计拓扑图信息
     * @param req
     * @return
     */
    @KeyLock(lockKeyParts = "topoId")
    @Override
    public ServiceResult designTopology(DesignTopologyReq req) {
        try {
            TopoTopology topoTopology = topoTopologyMapper.selectByPrimaryKey(req.getTopoId());
            if (topoTopology==null){
                return ServiceResult.fail(ErrorCodes.TOPO_LOGY_NOT_EXIST);
            }
            TopoTopology topology = new TopoTopology();
            topology.setTopoId(req.getTopoId());
            topology.setUpdateTime(new Date());
            topology.setUpdateUserId(getCurrentLoginUserId());
            topology.setXmlData(URLDecoder.decode(req.getXmlData(),"UTF-8"));
            topoTopologyMapper.updateByPrimaryKeySelective(topology);
            return ServiceResult.ok(req.getTopoId());
        } catch (UnsupportedEncodingException e) {
            throw new DuoJuHeException(ErrorCodes.FAIL);
        }
    }

    /**
     * 另存为拓扑图xml
     */
    @Override
    public void saveAsTopology(SaveAsTopologyReq req, HttpServletResponse response) throws Exception {
        OutputStream out = response.getOutputStream();
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
        try {
            if (StringUtils.isBlank(req.getFilename()) || StringUtils.isBlank(req.getTopoId())){
                PrintWriterUtil.pwPrintln(pw, ErrorCodes.PARAM_ERROR.getMessage());
                return;
            }
            TopoTopology topoTopology = topoTopologyMapper.selectByPrimaryKey(req.getTopoId());
            if (topoTopology==null){
                PrintWriterUtil.pwPrintln(pw, ErrorCodes.TOPO_LOGY_NOT_EXIST.getMessage());
                return;
            }
            response.setContentType("application/force-download");
            response.addHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", req.getFilename()));
            out.write(topoTopology.getXmlData().getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            PrintWriterUtil.pwPrintln(pw, ErrorCodes.FAIL.getMessage());
        } finally {
            pw.flush();
            pw.close();
            out.flush();
            out.close();
        }
    }
    /**
     * 根据拓扑图id查询拓扑图和分类组合信息
     * @param req
     * @return
     */
    @DataScopeFilter
    @Override
    public ServiceResult<InitEditTopologyRes> initEditTopologyByTopoId(TopoTopologyIdReq req,DataScopeFilterBean dataScope)  {
        try {
            TopoTopology topoTopology = topoTopologyMapper.selectByPrimaryKey(req.getTopoId());
            if (topoTopology==null){
                return ServiceResult.fail(ErrorCodes.TOPO_LOGY_NOT_EXIST);
            }
            //查询所有图元信息
            List<QueryTopoElementInfoRes> elementInfoResList = topoElementInfoMapper.queryTopoElementInfoResListByStatus(dataScope, SystemEnum.STATUS.NORMAL.getKey());
            //查询所有分类
            List<SelectTopoElementClassRes> elementClassList = topoElementClassMapper.querySelectTopoElementClassResListByStatus(dataScope, SystemEnum.STATUS.NORMAL.getKey());
            List<InitTopoElementClassRes> classResList = new ArrayList<>();
            for (SelectTopoElementClassRes elementClass : elementClassList) {
                //过滤图元
                List<QueryTopoElementInfoRes> children = elementInfoResList.stream().filter(e->elementClass.getClassId().equals(e.getClassId())).collect(Collectors.toList());
                InitTopoElementClassRes res = new InitTopoElementClassRes();
                res.setClassId(elementClass.getClassId());
                res.setClassName(elementClass.getClassName());
                res.setChildren(children);
                classResList.add(res);
            }
            InitEditTopologyRes initEditTopologyRes = new InitEditTopologyRes();
            initEditTopologyRes.setTopoName(topoTopology.getTopoName());
            initEditTopologyRes.setXmlData(topoTopology.getXmlData());
            initEditTopologyRes.setClassResList(classResList);
            return ServiceResult.ok(initEditTopologyRes);
        } catch (Exception e) {
            log.error("查询拓扑图出现异常：",e);
            return ServiceResult.fail(ErrorCodes.FAIL);
        }
    }

    /**
     * 根据拓扑图id查询拓扑图信息
     * @param req
     * @return
     */
    @Override
    public ServiceResult<ViewTopologyRes> queryViewTopologyByTopoId(TopoTopologyIdReq req)  {
        try {
            TopoTopology topoTopology = topoTopologyMapper.selectByPrimaryKey(req.getTopoId());
            if (topoTopology==null){
                return ServiceResult.fail(ErrorCodes.TOPO_LOGY_NOT_EXIST);
            }
            ViewTopologyRes viewTopologyRes = new ViewTopologyRes();
            viewTopologyRes.setTopoName(topoTopology.getTopoName());
            viewTopologyRes.setXmlData(topoTopology.getXmlData());
            return ServiceResult.ok(viewTopologyRes);
        } catch (Exception e) {
            log.error("查询拓扑图出现异常：",e);
            return ServiceResult.fail(ErrorCodes.FAIL);
        }
    }
}
